package blockboundary

import (
	"bytes"
	"encoding/binary"
	"math"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestCreateBlockBoundaries(t *testing.T) {
	tests := []struct {
		name        string
		queryShards int
		expected    [][]byte
	}{
		{
			name:        "single shard",
			queryShards: 1,
			expected: [][]byte{
				{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
				{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
			},
		},
		{
			name:        "multiple shards",
			queryShards: 4,
			expected: [][]byte{
				{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
				{0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
				{0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
				{0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
				{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
			},
		},
		{
			name:        "large number of evenly divisible shards",
			queryShards: 255,
		},
		{
			name:        "large number of not evenly divisible shards",
			queryShards: 1111,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			bb := CreateBlockBoundaries(tt.queryShards)

			if len(tt.expected) > 0 {
				require.Len(t, bb, len(tt.expected))
				for i := 0; i < len(bb); i++ {
					require.Equal(t, tt.expected[i], bb[i])
				}
			}

			maxDist := uint64(0)
			minDist := uint64(math.MaxUint64)

			// test that the boundaries are in order
			for i := 1; i < len(bb); i++ {
				require.True(t, bytes.Compare(bb[i-1], bb[i]) < 0)

				prev := binary.BigEndian.Uint64(bb[i-1][:8])
				cur := binary.BigEndian.Uint64(bb[i][:8])
				dist := cur - prev
				if dist > maxDist {
					maxDist = dist
				}
				if dist < minDist {
					minDist = dist
				}
			}

			// confirm that max - min <= 1. this means are boundaries are as fair as possible
			require.LessOrEqual(t, maxDist-minDist, uint64(1))
		})
	}
}
